Компиляционный синтез речи

LAWNMOWER MAN (Proteus)

Contents

Постановка задачи

Разработать систему автоматического синтеза речи по тексту

Существующие методы синтеза речи

Синтезаторы речи различаются, прежде всего, по исходному речевому материалу. Это могут быть или предварительно закодированная, сжатая по возможности речь в виде фраз, слов, слогов, фонем или сегментов, или искусственные речеподобные звуки, сформированные электронным устройством.

В первом случае используют широко известные из общей теории обработки сигналов способы: спектральное описание, адаптивную дельта модуляцию, клиппирование, логарифмическую импульсно-кодовую модуляцию, аппроксимацию формы полуволны и другие способы. Мне особенно нравится так называемый метод распознавания обазцов. Коротко его суть заключается в следующем: в памяти хранится множество образцов огибающих. Программа берет небольшую часть сигнала и решает на какой из образцов он похож больше, т.е. передается уже не сам сигнал, а номера образцов. Может быть это бестолковая идея, но её так просто реализовать !

По принципу построения синтезируемых сообщений из исходных элементов различают синтезаторы целостные и компилятивные. В первом случае целые слова или фразы, предварительно закодированной естественной речи извлекаются из памяти и восстанавливаются. Во втором случае система создает речевое сообщение из отдельных элементов естественного или искусственного происхождения. Целостный синтез обладает хорошей натуральностью звучания, но имеет ограниченный набор слов (фраз). Компилятивный синтез позволяет строить произвольные сообщения по тексту, но уступает в натуральности звучания, так как простая стыковка звуков не соответствует физике речеобразования и плохо воспринимается на слух. Системы компилятивного синтеза имеют очень важные преимущества:

-
компактность описания сообщений,
-
неограниченный словарь,
-
возможность синтеза речи по тексту.

Проще всего осуществлять такой синтез по фонетической транскрипции, или же система должна обладать способностью преобразовывать орфографический текст в фонетический.

Основная проблема компилятивного синтеза - это учет коартикуляции, т.е. взаимного влияния артикуляционных движений при произнесении соседних звуков и, как следствие, зависимости параметров фонемы от фонетического окружения. Особенно подвержены коартикуляции начальный и конечный участки фонем. Для решения этой проблемы разработаны различные методы. В частности, компилятивный синтез речи из аллофонов. Но в данном случае возникает другая проблема: предварительное выделение аллофонов из речи и их дальнейшее согласование при синтезе.

Методы решения, проблема коартикуляции:

Алгоритм поиска ударений

Таблица правил содержит в себе список эвристик, упорядоченный по приоритетам.

Для поиска ударений сначала проверяется список всегда выполняющихся эвристик, затем словарь исключений, затем список не всегда выполняющихся эвристик.

Список всегда выполняющихся эвристик:

  1. В односложных словах ударение падает на единственную гласную.

  2. В словах, содержащих гласную -e-, ударение падает на эту гласную.

  3. Если слово заканчивается на -льон (+ окончание), то ударение падает на этот слог.

    Пример: бульон, павильон.

  4. Если слово заканчивается на -ция, то ударение падает на предыдущий слог.

    Пример: милиция, конституция.

  5. Если слово заканчивается на -ион (+ окончание), то ударение падает на гласную -о-.

    Пример: миллион.

  6. Если слово заканчивается на -иант (+ окончание), то ударение падает на гласную -а-.

    Пример: бриллиант, вариант.

  7. Если слово начинается с приставки, и за приставкой следует одна из следующих подстрок: -бира-, -лага-, -тира-, -мира-, -пира-, -стила-, -дира-, то ударение падает последнюю гласную подстроки.

    Пример: разбирающий, перестилать.

  8. Если слово содержит подстроку -стира- или -ража-, то ударение падает на гласную -а-.

    Пример: стирающий, выражать.

  9. Если слово заканчивается на одну из следующих подстрок (+ окончание), то ударение падает на первую гласную подстроки: -ист-, -истк-, -изм-, -ент-, -ентк-, олог-, -ант-, -антк-, -уля-, -аст-, -астк-, -ир-, -ург-, -атор-, -итор-, -ктор-

    Пример: турист, туристка, капитализм, студент, студентка, филолог, аспирант, аспирантка, грязнуля, гимнаст, гимнастка, командир, хирург, оператор, аудитор, кондуктор.

  10. Если слово содержит один из следующих суффиксов, то ударение падает на первую гласную суффикса: -онок-, -онк-, -юшк-, -ишк-, -ашк-, -ат-, -ят-, -атин-, -ятин-, -оныш-, -яг-, -чан-,-чанин-,-янк-,-анк-,-рам- .

    Пример: медвежонок, девчонка, зверюшки, мальчишка, букашка, девчата, козлята, зайчатина, козлятина, работяга, англичанин, землянка, панорама.

  11. В словах, заканчивающихся на одну из следующих подстрок (+ окончание), ударение падает на первую гласную подстроки: -манность, -ебниц-, -иец-, -еец-, -аец-, -графия, -метрия, -логия, -евно, -ально, -ебно, -душно .

    Пример: туманность, лечебница, партиец, гвардеец, китаец, география, геометрия, биология, плачевно, нормально, лечебно, равнодушно.

  12. В словах, заканчивающихся на одну из следующих подстрок (+ окончание), ударение падает на первую гласную, стоящую перед подстрокой: -тельность, -енность, -льник-, -тик- .

    Пример: внимательность, болезненность, начальник, математика.

  13. Если слово состоит из двух слогов (+ окончание) и второй слог включает в себя одну из следующих подстрок, то ударение падает на первый слог: -очк-, -ость, -чик-, -щик-, -ник- .

    Пример: девочка, зрелость, мальчик, ящик, чайник.

  14. Если слово заканчивается на одну из следующих подстрок (+ окончание) то ударение падает на вторую гласную подстроки: -урги-, -овато .

    Пример: металлургия, серовато.

  15. В словах, начинающихся с подстроки -аэро-, за которой следует один слог (+ окончание), то ударение падает на этот слог.

    Пример: аэропорт, аэродром.

  16. Если слово заканчивается на одну из следующих подстрок и окончание прилагательного, то ударение падает на первую гласную подстроки: -евн-, -ебн-, -альн-, -ичн-, -учн-, -душн-, -ивн-, -ическ-, -он- анн н-, -инн-, -изн-, -ин-, -ив-,-ованн- .

    Пример: плачевный, лечебный, нормальный, логичный, научный, равнодушный, спортивный, классический, телефонная, глубинный, капризный, голубиный, ленивый, организованный.

  17. Если слово содержит один из следующих суффиксов, перед которым в слове только один слог, и заканчивается на окончание прилагательного, то ударение падает на этот суффикс: -аст-, -ист-.

    Пример: ушастый, пушистый.

  18. В словах, содержащих суффикс -оват- или -еват-, ударение падает на вторую гласную суффикса.

    Пример: сероватый, бежеватый.

  19. Если слово состоит из двух слогов и окончания прилагательного, и второй слог -айш- или -ейш-, то ударение падает на второй слог.

    Пример: ближайший, нежнейший.

  20. Если слово заканчивается на одну из следующих подстрок и окончание прилагательного, то ударение падает на слог, предшествующий подстроке: -тельск-, -льск-.

    Пример: приятельский, подольский.

  21. Если слово заканчивается на подстроку - и окончание прилагательного или на подстроку - анно, и перед подстрокой в слове стоит только один слог то ударение падает на первую гласную подстроки.

    Пример: туманный, желанно.

  22. Если слово начинается с приставки, заканчивается на одну из следующих подстрок и окончание глагола, и между приставкой и подстрокой стоит по меньшей мере один слог, то ударение падает на слог, предшествующий подстроке: -ива-, -ыва-, -ив-, -ыв-.

    Пример: рассматриваешь, разглядываешь, расписываться, приклеивать.

  23. Если слово начинается с приставки, заканчивается на одну из следующих подстрок и окончание прилагательного, и между приставкой и подстрокой стоит по меньшей мере один слог, то ударение падает на слог, предшествующий подстроке: -ивающ-, -ывающ-, -иваем-, -ываем-, -ивавш-, -ывавш- .

    Пример: рассматривающий, разглядывающий, разбрасываемый, привязывавший.

  24. Если слово заканчивается на подстроку - иров - и окончание глагола, и перед подстрокой в слове стоит по меньшей мере один слог, то ударение падает на первую гласную подстроки.

    Пример: регулировать, трансформировать.

  25. Если слово заканчивается на одну из следующих подстрок и окончание прилагательного, и перед подстрокой в слове стоит по меньшей мере два слога, то ударение падает на первую гласную подстроки: -ирующ-, -ируем-, -ировавш-.

    Пример: регулирующий, трансформируемый, гарантировавший.

Список не всегда выполняющихся эвристик:

  1. В глаголах, имеющих следующие окончания -ишь-, -ешь-, -ите-, -ете-, -ем-, -ет-, -им-, -ит-, -ат-, -ят-, -ут-, -ют- ударение падает на слог, предшествующий окончанию, в остальных глаголах (кроме указанных в словаре) ударение падает на первый слог окончания.

    Пример: смотришь, ходят.

  2. Если прилагательное имеет суффикс - ов -, перед которым в слове стоит только один слог, то ударение падает на этот суффикс.

    Пример: торговый, портовый.

  3. Если существительное заканчивается на одну из следующих подстрок и окончание, то ударение падает на первую гласную подстроки: -ани-, -ени-, -яни-, -ити-, -яти- .

    Пример: внимание, строение, влияние, наитие, мероприятие.

  4. В существительных, заканчивающихся на -арь или -ырь, ударение падает на последний слог.

    Пример: фонарь, пустырь.

  5. В существительных, заканчивающихся на подстроку - шинств - и окончание, ударение падает на первую гласную окончания.

    Пример: старшинство, меньшинство.

  6. В существительных, состоящих из двух слогов и окончания, где второй слог - суффикс - ец -, ударение падает на первую гласную окончания. Если же окончание нулевое,то ударение падает на суффикс.

    Пример: гордец, гордеца.

  7. В существительных, имеющих один из следующих суффиксов, ударение падает на слог, предшествующий суффиксу: -чик-, -щик-, -ник- .

    Пример: вагончик, барабанщик, второгодник.

  8. В словах, содержащих подстроку - енн -, ударение падает на слог, предшествующий подстроке.

    Пример: заполненный, присмотренный.

  9. В словах, содержащих суффикс - чив -, ударение падает на слог, предшествующий суффиксу.

    Пример: задумчиво, находчивый.

  10. В словах, заканчивающихся на одну из следующих подстрок и окончание, ударение падает на слог, предшествующий подстроке: -чивост-, -ост-.

    Пример: находчивость, туманность.

  11. В наречиях, заканчивающихся на -иво, -асто, -исто, -турно ударение падает на первую гласную подстроки.

    Пример: красиво, пушисто, культурно.

  12. В прилагательных, имеющих один из следующих суффиксов, стоящий перед окончанием, ударение падает на суффикс: -аст-, -ист-, -лив-, -уч-, -ив-.

    Пример: ушастый, душистый, сварливый, могучий,игривый.

  13. В существительных, заканчивающихся на - ель или -аль (+ окончание), ударение падает на первую гласную подстроки.

    Пример: параллель, спираль.

  14. В существительных, имеющих суффиксы -ушк-, -иц-, -чих-, -еньк-, -ышк-, -очек-, -ищ-, -овк-, -ечк-, ударение падает на суффикс.

    Пример: деревушка, ключица, зайчиха, деревенька, мартышка, дружочек, ручища, курсовка, крылечко.

  15. В существительных, имеющих суффиксы -чиц-, -ниц-, ударение падает на слог, предшествующий суффиксу.

    Пример: мотажница, наладчица.

  16. Если слово содержит подстроку - тель -, то ударение падает на слог, предшествующий подстроке.

    Пример: обстоятельство, старательно.

  17. Если существительное заканчивается на одну из следующих подстрок (+ окончание), то ударение падает на слог, предшествующий подстроке: -ств-, -оньк-, -тик-, -нец, -нц-, -ик- .

    Пример: хулиганство, кухонька, зонтик, румянец,румянца, коврик.

  18. В прилагательных, заканчивающихся на одну из следующих подстрок и окончание, ударение падает на слог, пред- шествующий подстроке: -еньк-, -ищн-, -ск-, -ечн-, -чн-.

    Пример: беленький, чудовищный, житейский, точечный, отличный.

  19. В существительных, оканчивающихся на подстроку -тек - и окончание или на подстроку - тур - и окончание, ударение падает на первую гласную подстроки.

    Пример: картотека, партитура.

  20. В существительных, заканчивающихся на - ок, ударение падает на этот слог.

    Пример: дружок, бережок.

  21. В существительных, оканчивающихся на - брь, ударение падает на последний слог. Если окончание не нулевое, то ударение падает на первую гласную окончания.

    Пример: октябрь, декабря.

  22. В существительных, имеющих подстроку -парк-, ударение падает на первую гласную подстроки.

    Пример: зоопарк, лесопарк.

Реализация поиска ударений.

Программа написана на языке C++, с применением библиотек WATCOM C++.

Программа построена таким образом, что она ничего не знает о языке, с которым она работает. Все необходимые данные записаны в конфигурационном файле.
Управляющие слова конфигурационного файла:
REMARKCHARустановка символа комментария(по умолчанию '.').
SYLLABLE установка символа обозначающего слог(по умолчанию '@').
ANYCHAR установка символа обозначающего любую непустую последовательность букв (по умолчанию '-').
PUBLIC установка множества гласных букв.
DEFTOKS установка символа обозначающего какое либо буквосочетание из указанного списка (применяется для обозначения приставок, окончаний и т.п.). Символ равный (ANYCHAR) обозначает возможность пустого буквосочетания.
OBLIGATORYсписок всегда выполняющихся эвристик (список оканчивается словом END).
!OBLIGATORY список не всегда выполняющихся эвристик.
EXCEPTIONSфайл со словарем исключений.
END. (chc) конец файла (chc контрольная сумма файла).

Комментарии начинаются и кончаются с помощью символа определенного REMARKCHAR.

Структура эвристики:

Список методов класса Analizer.

Analizer(char *);

Конструктор. В качестве параметра принимает имя конфигурационного файла, который загружает в память.

~Analizer();

Деструктор.

RemoveList(toksType *);

Часть деструктора. Удаляет список из памяти.

char Accent(char *);

Принимает указатель на слово. Выдает позицию ударения.

void AddToks(ifstream &);

Добавляет слово в список оператора DEFTOKS.

void AddHeuristic(ifstream &, toksType **);

Загружает список эвристик. Принимает указатель на файловый поток и список эвристик.

bool Find(char *, char *, char );

Сверяет слово с заданной эвристикой. Принимает указатель на эвристику, слово, и начальную позицию в слове (0).

bool FindTok(char *, char *, char *, char );

Сверяет слово буквенным шаблоном из списка DEFTOKS и с остатком эвристики. Принимает указатель на остаток эвристики, остаток слова, образец из списка, текущую позицию слова.

char *FindInList(toksType *, char *);

Ищет заданное слово в заданном списке эвристик. Возвращает указатель на эвристику или 0. Также устанавливает переменные Direction, Position.

void ReadBuffer(ifstream &file)

Читает очередное слово из конфигурационного файла, игнорируя комментарии.

Список переменных класса Аnalizer.

int CheckSum;

Контрольная сумма файла с правилами

char Position;

Приблизительная позиция ударения.

bool Direction;

Направление поиска гласной, от приблизительной позиции ударения.

char Public[30];

Список гласных.

char toks[10];

Список символов DEFTOKS.

char toksQuantity;

Количество определенных DEFTOKS.

struct toksType{toksType *next; char *val;};

Элемент списка.

toksType *toksList[10];

Массив указателей на списки DEFTOKS.

toksType *Obligatory, *NotObligatory, *Exceptions;

Ссылка на список обязательных и не обязательных эвристик. А также словарь исключений.

String buffer;

Буфер общего назначения.

char AnySyms,RemarkSym,SyllableSym;

Значения символов ANYCHAR, REMARKCHAR, SYLLABLE.

Пример использования класса Analizer


File: main.cpp #ifndef __WATCOMC__ #error Sorry this program use only with Watcom C++ #endif #define _main_ #define _debug #include<fstream.h> #include<string.h> #include<string.hpp> #include"analizer.cpp" void main() { Analizer analiz("setup.txt"); for(;;) { char word[100]; cin>>word; if(cin.eof()) return; if(!strcmp(word,".system")) return; char pos=analiz.Accent(word); for(char t=0; t[word]; t++) { cout<<word[t]; if(t==pos) cout<<(char)39;}; cout<<endl; } } End of file: main.cpp


Контроль корректности эвристик

Поскольку существует возможность, что эвристики с более высоким приоритетом, будут полностью исключать возможность срабатывания эвристик с более низким приоритетом. Например в списке правил :
x-z
xz-y
В данном примере использование первой эвристики делает бессмысленным использование второй. Программа пытается распознать подобные ситуации. Для этого очередная загруженная эвристика преобразуется по следующим правилам :
[x][x]x буква
-β - тот самый который обозначает любую последовательность
@α слог
#β окончание, приставка, ...
Где :
βсимвол который не совпадает ни с одним символом алфавита
α символ который не совпадает ни с одним символом алфавита, но считается гласной буквой

После чего полученное слово проверяется на совпадение всеми предыдущими эвристиками. Таким образом система выявляет все холостые эвристики в момент загрузки (две там точно есть).

О словаре

Что можно сделать если словарь в память на лезет. Тут можно пойти двумя путями.

  1. Попытаться засунуть его в память.
  2. Сделать его выносным.

Компактный способ хранения словаря

Символ с кодом <32 обозначает конец слова, и одновременно говорит о том в какой позиции следующее слово отличается от текущего.
базисная [0]базисна
базовые [3]овые
бинарные [1]инарные
биноминальные [3]оминальные
Таким образом чем больше слов, тем меньше отличий, тем больше упаковка. Так как мы имеем дело с русскими словами, то числами от 32 до 127 можно закодировать наиболее распространенные окончания слов.

Выносной словарь

Здесь наша главная задача найти слово за полиномиальное время (такое умное слово знаю !). То есть отказаться от линейного поиска и реализовать одну из описанных ниже способов хранения слов :

Наш словарь исключений

Числительные

Названия чисел

1один
2два
3три
4четыре
5пять
6шесть
7семь
8восемь
9девять
10десять
11одиннадцать
12двенадцать
13тринадцать
14четырнадцать
15пятнадцать
16шестнадцать
17семнадцать
18восемнадцать
19девятнадцать
10десять
20двадцать
30тридцать
40сорок
50пятьдесят
60шестьдесят
70семьдесят
80восемьдесят
90девяносто
100сто
200двести
300триста
400четыреста
500пятьсот
600шестьсот
700семьсот
800восемьсот
900девятьсот

103 тысяча
106 миллион
109 миллиард
1012биллион
1015квадриллион
1018квинтиллион
1021секстиллион
1024септиллион
1027октиллион

Для справки. Откуда берутся названия больших чисел. Берете количество ноликов, делите на три, вычетаете еденицу. И произносите получившееся число на латыни.

Фонетическая транскрипция

Исходным материалом для алгоритма транскрипции являются :

  1. алфавит русских букв A

  2. алфавит транскрипционных букв V

  3. совокупность {x → y} постановок x → y, где x - слово в алфавите A, y - в алфавите V.

  4. номер ударной гласной в слове : f(t).

На совокупность подстановок, вы можете полюбоваться посмотрев .DVI файл. Запихивать их в HTML мне просто в лом. После долгой борьбы c 'Explorer' мне не удалось изобразить на экране знак 'не принадлежит'.

В работе программы можно выделить три стадии :

  1. программа читает из конфигурационного файла запись правил в инфиксной форме и строит из них деревья;

  2. строит из деревьев специальную бинарную запись правил (запись которая жрет меньше и работает быстрее);

  3. программа работает используя подготовленные таким образом правила

1. Чтение правил

Обычное правило подстановки выглядит приблизительно так :

m=f(t)^(m=1 | m#1^e(-1)~A2^X1 | m=n | m#n^e(1)~(A2\A3)^X2)

операнды подстановки
m позиция буквы в слове
f, f(x) позиция ударения
n длина слова
a,b,c служебные ячейки. Используются в циклах
e текущая буква в слове
e(-3), e(1)буква рядом с текущей
0...99 цифра
A1...A99 множество
X1...X9 переопределяемое правило
{a 5 n} коньюктивный цикл
[a 3 c] дизьюктивный цикл
= word равен истине если слово равно образцу
# word равен истине если слово не равно образцу
операции
знакприоритетназначение
= 4равенство
# 4неравенство
~ 6принадлежность множеству
$ 6непринадлежность множеству
U 4объеденение множеств
@ 3пересечение множеств
| 8или
^ 7и
\ 5вычетание множеств
+ 3сложение чисел
- 3вычитание
( ) -скобки

Операции с меньшим приоритетом выполняются раньше.

В программе можно ввести такие условия как :

В тексте они записываются так :

В общем у условия три параметра, параметры можно разделять пробелами.

  1. a,b,c ячейка которая используется как счетчик
  2. 0-9 начальное значение (цифра)
  3. a,b,c,n,m,f предельное значение цикла (ячейка).

    n длина слова

    m позиция буквы для которой применяется правило

    f позиция ударения в слове

А вот мой маленький набросок :

Этот набросок читает формулу и строит в памяти такую фигню :

Sicret наш маленький секрет !!!

Для прикола <словарь произношения программы Magic gooddy>

Синтез

Ну здесь пока ничего хорошего не получается. В смысле пока ничего здесь не делал.

В принципе идеально подходит формантный синтез. Но на первый раз выбор остановлен не линейной коомбинации фонем. Если f1(t), f2(t) сигналы первой и второй фонемы. τ общее время перехода. То результирующий сигнал на стыке описывается формулой

f=f1(t)*cos(t/τ*π/2)²+f2(t)*sin(t/τ*π/2)²

А можно даже просто :

f=f1(t)*t/τ+f2(t)*(1-t/τ)

Для слуха эти две формулы сильной разницы не имеют. Короче говоря иногда мне удавалось собрать небольшое слово так, чтобы оно звучало прилично. Но в основном результат мрачный. Сами фонемы можно немного сжать. Мои небольшие эксперименты показывают что, если в взять кривую как некий многочен и запомнить координаты его вершин, но можно воспроизвести сигнал соединяя указанные вершины сплайнами. Или, если не не хочется напрягаться, просто палками, 'о чем идет речь' все равно будет понятно.

О HTML

Меня от него тошнит. Особенно мне не нравится то, что в нем нельзя определять макрокоманды. Чтобы написать заголовок нужно каждый раз писать :

<h1 align="center"><font color="blue"><u>Название</u></font></h1>

Ну меня это просто бесит !



Please send any comments or corrections to my mail [ ]

[source code]

Hosted by uCoz